package jrain.fw.script.tools;

import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public abstract class EnumeratedMap implements Map<Object, Object> {
	// -------------------------------------
	// Member variables
	// -------------------------------------

	Map<Object, Object> mMap;

	// -------------------------------------

	public void clear() {
		throw new UnsupportedOperationException();
	}

	// -------------------------------------

	public boolean containsKey(Object pKey) {
		return getValue(pKey) != null;
	}

	// -------------------------------------

	public boolean containsValue(Object pValue) {
		return getAsMap().containsValue(pValue);
	}

	// -------------------------------------

	public Set<java.util.Map.Entry<Object, Object>> entrySet() {
		return getAsMap().entrySet();
	}

	// -------------------------------------

	public Object get(Object pKey) {
		return getValue(pKey);
	}

	// -------------------------------------

	public boolean isEmpty() {
		return !enumerateKeys().hasMoreElements();
	}

	// -------------------------------------

	public Set<Object> keySet() {
		return getAsMap().keySet();
	}

	// -------------------------------------

	public Object put(Object pKey, Object pValue) {
		throw new UnsupportedOperationException();
	}

	// -------------------------------------

	public void putAll(Map<?, ?> pMap) {
		throw new UnsupportedOperationException();
	}

	// -------------------------------------

	public Object remove(Object pKey) {
		throw new UnsupportedOperationException();
	}

	// -------------------------------------

	public int size() {
		return getAsMap().size();
	}

	// -------------------------------------

	public Collection<Object> values() {
		return getAsMap().values();
	}

	// -------------------------------------
	// Abstract methods
	// -------------------------------------

	/**
	 * Returns an enumeration of the keys
	 */
	public abstract Enumeration<?> enumerateKeys();

	// -------------------------------------

	/**
	 * Returns true if it is possible for this data source to change
	 */
	public abstract boolean isMutable();

	// -------------------------------------

	/**
	 * Returns the value associated with the given key, or null if not found.
	 */
	public abstract Object getValue(Object pKey);

	// -------------------------------------

	/**
	 * Converts the MapSource to a Map. If the map is not mutable, this is cached
	 */
	public Map<Object, Object> getAsMap() {
		if (mMap != null) {
			return mMap;
		} else {
			Map<Object, Object> m = convertToMap();
			if (!isMutable()) {
				mMap = m;
			}
			return m;
		}
	}

	// -------------------------------------

	/**
	 * Converts to a Map
	 */
	Map<Object, Object> convertToMap() {
		Map<Object, Object> ret = new HashMap<Object, Object>();
		for (Enumeration<?> e = enumerateKeys(); e.hasMoreElements();) {
			Object key = e.nextElement();
			Object value = getValue(key);
			ret.put(key, value);
		}
		return ret;
	}

	// -------------------------------------
}
